/*
 * Copyright 2007 Agile Plugins Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * 	http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.googlecode.agileplugins.monkey;

import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiCallExpression;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiElementFactory;
import com.intellij.psi.PsiExpression;
import com.intellij.psi.PsiExpressionList;
import com.intellij.psi.PsiField;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.PsiMethodCallExpression;
import com.intellij.psi.PsiNewExpression;
import com.intellij.psi.PsiParameter;
import com.intellij.psi.PsiParameterList;
import com.intellij.psi.PsiPrimitiveType;
import com.intellij.psi.PsiReferenceExpression;
import com.intellij.psi.PsiStatement;
import com.intellij.psi.PsiType;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;

import java.util.HashMap;
import java.util.Map;

public class CreateLocalVariablesForParameterIntention extends BaseIntention {

    @NotNull
    public String getText() {
        return "Create local variables for parameters";
    }

    @NotNull
    public String getFamilyName() {
        return "PurpleMonkeyDishwasher";
    }

    public boolean isAvailable(Project project, Editor editor, PsiFile file) {
        PsiElement element = getElement(editor, file);
        PsiElement callExpression = getContainingElement(element, PsiCallExpression.class);
        return callExpression != null;
    }

    public void invoke(Project project, Editor editor, PsiFile file) throws IncorrectOperationException {
        try {
            PsiElement element = getElement(editor, file);
            PsiCallExpression callExpression = (PsiCallExpression) getContainingElement(element, PsiCallExpression.class);
            String text = null;
            PsiMethod method = null;
            PsiMethod[] possibleMethods = null;
            PsiClass psiClass = (PsiClass) getContainingElement(element, PsiClass.class);
            if (callExpression instanceof PsiMethodCallExpression) {
                PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression) callExpression;
                PsiReferenceExpression psiReferenceExpression = methodCallExpression.getMethodExpression();
                method = (PsiMethod) psiReferenceExpression.resolve();
                if (method == null) {
                    PsiExpression qualifierExpression = psiReferenceExpression.getQualifierExpression();
                    PsiType type = qualifierExpression.getType();
                    PsiClass aClass = qualifierExpression.getManager().findClass(type.getCanonicalText(), GlobalSearchScope.allScope(project));
                    possibleMethods = aClass.findMethodsByName(psiReferenceExpression.getReferenceName(), true);
                }
                text = methodCallExpression.getMethodExpression().getText();
            } else if (callExpression instanceof PsiNewExpression) {
                PsiNewExpression psiNewExpression = (PsiNewExpression) callExpression;
                PsiClass constructorClass = (PsiClass) psiNewExpression.getClassReference().resolve();
                method = psiNewExpression.resolveConstructor();
                if (method == null) {
                    possibleMethods = constructorClass.getConstructors();
                }
                text = "new " + psiNewExpression.getClassReference().getText();
            }
            if (method == null && possibleMethods != null) {
                for (int i = 0; i < possibleMethods.length; i++) {
                    PsiMethod psiMethod = possibleMethods[i];
                    if (psiMethod.getParameterList().getParametersCount() > 0) {
                        method = psiMethod;
                        break;
                    }
                }
            }
            if (method == null) {
                return;
            }
            PsiField[] psiFields = psiClass.getFields();
            Map map = new HashMap();
            for (int i = 0; i < psiFields.length; i++) {
                PsiField psiField = psiFields[i];
                PsiType type = psiField.getType();
                if (type instanceof PsiPrimitiveType || type.getCanonicalText().startsWith("java.")) {
                    continue;
                }
                map.put(type, psiField.getName());
            }
            PsiElementFactory elementFactory = element.getManager().getElementFactory();
            PsiParameterList list1 = method.getParameterList();
            PsiParameter[] parameters = list1.getParameters();
            StringBuffer sb = new StringBuffer();
            sb.append(text).append('(');
            for (int i = 0; i < parameters.length; i++) {
                PsiParameter parameter = parameters[i];
                if (i > 0) {
                    sb.append(", ");
                }
                String name = (String) map.get(parameter.getType());
                if (name == null) {
                    name = getParameterName(parameter);
                }
                sb.append(name);
            }
            sb.append(")");
            PsiExpression psiExpression = callExpression.getManager().getElementFactory().createExpressionFromText(sb.toString(), callExpression);
            PsiCallExpression xxx = (PsiCallExpression) callExpression.replace(psiExpression);
            PsiExpressionList expressionList = xxx.getArgumentList();
            PsiExpression[] psiExpressions1 = expressionList.getExpressions();
            for (int i = 0; i < psiExpressions1.length; i++) {
                PsiExpression expression = psiExpressions1[i];
                if (expression instanceof PsiReferenceExpression) {
                    PsiReferenceExpression psiReferenceExpression = (PsiReferenceExpression) expression;
                    PsiElement psiElement = psiReferenceExpression.resolve();
                    if (psiElement == null) {
                        PsiParameter parameter = parameters[i];
                        PsiStatement statement = elementFactory.createStatementFromText(parameter.getType().getPresentableText() + " " + getParameterName(parameter) + ";", parameter);
                        System.out.println("statement = " + statement);
                        PsiStatement callStatement = getContainingStatement(xxx);
                        System.out.println("callStatement = " + callStatement);
                        callStatement.getParent().addBefore(statement, callStatement);
                    }
                }
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private PsiStatement getContainingStatement(PsiElement callExpression) {
        while (callExpression != null && (!(callExpression instanceof PsiStatement))) {
            callExpression = callExpression.getParent();
        }
        return (PsiStatement) callExpression;
    }

    private String getParameterName(PsiParameter parameter) {
        String name = parameter.getName();
        if (name == null) {
            name = StringUtil.lcFirst(parameter.getType().getPresentableText());
        }
        return name;
    }

    public boolean startInWriteAction() {
        return true;
    }
}
